package com.ruoyi.handler.socket;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ByteUtil;
import com.ruoyi.common.constant.AlarmLevel;
import com.ruoyi.common.constant.AlarmResolveStatus;
import com.ruoyi.common.constant.AlarmType;
import com.ruoyi.common.constant.socket.MessageType;
import com.ruoyi.common.core.domain.entity.socket.bean.*;
import com.ruoyi.common.core.domain.entity.system.*;
import com.ruoyi.common.utils.MyByteUtil;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.handler.websocket.WebSocketMessageHandler;
import com.ruoyi.manager.SocketChannelManager;
import com.ruoyi.system.service.IBoatAlarmInfoService;
import com.ruoyi.system.service.ISysWharfService;
import com.ruoyi.system.service.IUserBoatOrderInfoService;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;

import java.math.BigDecimal;
import java.nio.ByteOrder;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
public class SocketMessageHandler {

    /**
     * 报文编号。
     */
    private static AtomicInteger MessageSeq = new AtomicInteger(0);

    private static long BaseCoordinateValue = Long.parseUnsignedLong("7fffffff", 16); // 2147483647


    /**
     * 解析船只注册信息-配置报文。
     */
    public static void parseBoatRegisterInfo(ByteBuf in, BoatRegisterInfo boatRegisterInfo) {
        //读取配置读操作 2个bit。取值范围：0-3，可能取值：0：无操作，1：配置读，3：无效值
        byte readConfig = MyByteUtil.getBitValueFromByte(in.readByte(), 6, 7);
        //读取配置报文应答 2个bit。0：无操作，1：报文应答，2：报文未应答，3：无效值
        byte responseConfig = MyByteUtil.getBitValueFromByte(in.readByte(), 6, 7);
        //保留 6个字节
        in.skipBytes(6);

        //读取船只出厂注册id 16个字节
        StringBuilder boatIdentityIdBuilder = new StringBuilder();
        for (int i = 0; i < 16; i++) {
            byte itemByte = in.readByte();
            if (itemByte != 10) { //10='\n'，即不为换行符，因为是以换行符结尾
                boatIdentityIdBuilder.append((char) itemByte);
            } else {
                break;
            }
        }
        if (16 - boatIdentityIdBuilder.length() > 0) {
            in.skipBytes(16 - boatIdentityIdBuilder.length() - 1); //要将上一步剩余未读的位给读完
        }
        String boatIdentityId = boatIdentityIdBuilder.toString().trim();
        log.info("boatIdentityId: {}", boatIdentityId);

        boatRegisterInfo.setBoatIdentityId(boatIdentityId);
        boatRegisterInfo.setReadFlag(readConfig);
        boatRegisterInfo.setResponseFlag(responseConfig);
        log.info("parseBoatRegisterInfo boatRegisterInfo= {}", boatRegisterInfo);
    }

    public static void encodeBoatRegisterInfo(ByteBuf out, BaseDataPiece dataPiece) {
        BoatRegisterInfo boatRegisterInfo = (BoatRegisterInfo) dataPiece;
        //写入配置读操作 2个bit。
        out.writeByte(boatRegisterInfo.getReadFlag());
        //写入配置报文应答 2个bit。
        out.writeByte(boatRegisterInfo.getResponseFlag());
        //保留 6个字节
        for (int i = 0; i < 6; i++) {
            out.writeByte(0xff);
        }

        //写入船只id 16个字节
        String boatIdentityId = boatRegisterInfo.getBoatIdentityId();
        int boatIdLength = boatIdentityId.length();
        for (int i = 0; i < 16; i++) {
            if (i < boatIdLength) {
                out.writeByte(boatIdentityId.charAt(i));
            } else {
                out.writeByte(0);
            }
        }
    }

    /**
     * 解析船只基本配置信息报文。
     *
     * @param in
     * @param boatBasicConfigInfo
     */
    public static void parseBoatBasicConfigInfo(ByteBuf in, BoatBasicConfigInfo boatBasicConfigInfo) {
        //读取配置读操作 2个bit。取值范围：0-3，可能取值：0：无操作，1：配置读，3：无效值
        byte readConfig = MyByteUtil.getBitValueFromByte(in.readByte(), 6, 7);
        //读取配置报文应答 2个bit。0：无操作，1：报文应答，2：报文未应答，3：无效值
        byte responseConfig = MyByteUtil.getBitValueFromByte(in.readByte(), 6, 7);
        //保留 6个字节
        in.skipBytes(6);

        //读取船只注册id 16个字节
        StringBuilder boatIdentityIdBuilder = new StringBuilder();
        for (int i = 0; i < 16; i++) {
            byte itemByte = in.readByte();
//            log.info("itemByte0: {}", itemByte);

            if (itemByte != 10) { //10='\n'，即不为换行符，因为是以换行符结尾
                boatIdentityIdBuilder.append((char) itemByte);
            } else {
                break;
            }
        }
        if (16 - boatIdentityIdBuilder.length() > 0) {
            in.skipBytes(16 - boatIdentityIdBuilder.length() - 1); //要将上一步剩余未读的位给读完
        }
        String boatIdentityId = boatIdentityIdBuilder.toString().trim();
//        log.info("boatIdentityId: {}", boatIdentityId);

        //读取船只出厂日期时间。6个字节
        Date prodDate = readTimeFromByteBuf(in);

        //保留 2个字节
        in.skipBytes(2);

        //读取电机编号 16个字节
        StringBuilder motorBuilder1 = new StringBuilder();
        for (int i = 0; i < 16; i++) {
            byte itemByte = in.readByte();
//            log.info("itemByte1: {}", itemByte);

            if (itemByte != 10) { //10='\n'，即不为换行符，因为是以换行符结尾
                motorBuilder1.append((char) itemByte);
            } else {
                break;
            }
        }
        if (16 - motorBuilder1.length() > 0) {
            in.skipBytes(16 - motorBuilder1.length() - 1); //要将上一步剩余未读的位给读完
        }
        String motorCode1 = motorBuilder1.toString().trim();
//        log.info("motorCode1: {}", motorCode1);

        //读取电池编号 16个字节
        StringBuilder batteryBuilder = new StringBuilder();
        for (int i = 0; i < 16; i++) {
            byte itemByte = in.readByte();
//            log.info("itemByte2: {}", itemByte);
//            batteryBuilder.append((char) itemByte);

            if (itemByte != 10) { //10='\n'，即不为换行符，因为是以换行符结尾
                batteryBuilder.append((char) itemByte);
            } else { //遇到换行符'\n'后面就不读了
                break;
            }
        }
        if (16 - batteryBuilder.length() > 0) {
            in.skipBytes(16 - batteryBuilder.length() - 1); //要将上一步剩余未读的位给读完
        }
        String batteryCode = batteryBuilder.toString().trim();
//        log.info("batteryCode: {}", batteryCode);

        //读取电池编号 8个字节
        StringBuilder boatCodeBuilder = new StringBuilder();
        for (int i = 0; i < 8; i++) {
            byte itemByte = in.readByte();
//            log.info("itemByte3: {}", itemByte);

            if (itemByte != 10) { //10='\n'，即不为换行符，因为是以换行符结尾
                boatCodeBuilder.append((char) itemByte);
            } else { //遇到换行符'\n'后面就不读了
                break;
            }
        }
        if (8 - boatCodeBuilder.length() > 0) {
            in.skipBytes(8 - boatCodeBuilder.length() - 1); //要将上一步剩余未读的位给读完
        }
        String boatCode = boatCodeBuilder.toString().trim();
//        log.info("boatCode: {}", boatCode);

        //读取船只编号 占8个字节，但只取前3个字节，因为当前船只编号格式类似于A01这种格式
//        StringBuilder builder2 = new StringBuilder();
//        for (int i = 0; i < 3; i++) {
//            builder2.append((char) in.readByte());
//        }
//        for (int i = 0; i < 5; i++) {
//            in.readByte();
//        }
//        String boatCode = builder2.toString();

        //读取泊位纬度 4个字节
        in.skipBytes(4);

        //读取泊位经度 4个字节
        in.skipBytes(4);

        //读取泊位方向 2个字节
        in.skipBytes(2);

        //读取刻录机IP 4个字节
        short ip1 = in.readUnsignedByte();
        short ip2 = in.readUnsignedByte();
        short ip3 = in.readUnsignedByte();
        short ip4 = in.readUnsignedByte();
        String recorderIp = ip1 + "." + ip2 + "." + ip3 + "." + ip4;

        //保留 2个字节
        in.skipBytes(2);

        //读取录像机编号 占16个字节，但录像机编号通常长度为9位，因此目前固定只读前面的9位
        StringBuilder recorderCodeBuilder = new StringBuilder();
        for (int i = 0; i < 16; i++) {
            byte itemByte = in.readByte();
//            log.info("itemByte4: {}", itemByte);

            if (itemByte != 10) { //10='\n'，即不为换行符，因为是以换行符结尾
                recorderCodeBuilder.append((char) itemByte);
            } else { //遇到换行符'\n'后面就不读了
                break;
            }
        }
        if (16 - recorderCodeBuilder.length() > 0) {
            in.skipBytes(16 - recorderCodeBuilder.length() - 1); //要将上一步剩余未读的位给读完
        }
        String recorderCode = recorderCodeBuilder.toString().trim();
//        log.info("recorderCode: {}", recorderCode);

        //读取电机2编号 16个字节
//        StringBuilder motorBuilder2 = new StringBuilder();
//        for (int i = 0; i < 16; i++) {
//            byte itemByte = in.readByte();
//            log.info("itemByte1: {}", itemByte);
//
//            if (itemByte != 10) { //10='\n'，即不为换行符，因为是以换行符结尾
//                motorBuilder2.append((char) itemByte);
//            } else {
//                break;
//            }
//        }
//        if (16 - motorBuilder2.length() > 0) {
//            in.skipBytes(16 - motorBuilder2.length() - 1); //要将上一步剩余未读的位给读完
//        }
//        String motorCode2 = motorBuilder2.toString().trim();
//        log.info("motorCode2: {}", motorCode2);

        boatBasicConfigInfo.setReadFlag(readConfig);
        boatBasicConfigInfo.setResponseFlag(responseConfig);
        boatBasicConfigInfo.setBoatIdentityId(boatIdentityId);
        boatBasicConfigInfo.setProduceDate(prodDate);
        boatBasicConfigInfo.setMotorCode1(motorCode1);
        boatBasicConfigInfo.setBatteryCode(batteryCode);
        boatBasicConfigInfo.setBoatCode(boatCode);
        boatBasicConfigInfo.setRecorderCode(recorderCode);
        log.info("parseBoatBasicConfigInfo boatBasicConfigInfo: {}", boatBasicConfigInfo);
    }

    public static void encodeBoatBasicConfigInfo(ByteBuf out, BaseDataPiece dataPiece) {
        BoatBasicConfigInfo boatBasicConfigInfo = (BoatBasicConfigInfo) dataPiece;
        //写入配置读操作 2个bit。
        out.writeByte(boatBasicConfigInfo.getReadFlag());
        //写入配置报文应答 2个bit。
        out.writeByte(boatBasicConfigInfo.getResponseFlag());
        //保留 6个字节
        for (int i = 0; i < 6; i++) {
            out.writeByte(0xff);
        }
        //出厂ID 16个字节
        String boatIdentityId = boatBasicConfigInfo.getBoatIdentityId();
        for (int i = 0; i < 16; i++) {
            if (i < boatIdentityId.length()) {
                out.writeByte(boatIdentityId.charAt(i));
            } else {
                out.writeByte(0);
            }
        }

        //中间40个字节
        for (int i = 0; i < 40; i++) {
            out.writeByte(0xff);
        }
        //船编号 8个字节 3+5
        String boatCode = boatBasicConfigInfo.getBoatCode();
        for (int i = 0; i < 8; i++) {
            if (i < boatCode.length()) {
                out.writeByte(boatCode.charAt(i));
            } else {
                out.writeByte(0);
            }
        }

        //其余48个字节
        for (int i = 0; i < 16; i++) {
            out.writeByte(0xff);
        }
        for (int i = 0; i < 16; i++) {
            out.writeByte(0xff);
        }
        //写入电机编号2,16个字节
        for (int i = 0; i < 16; i++) {
            out.writeByte(0xff);
        }
    }


    /**
     * 解析泡泡豚船只信息状态报文。
     *
     * @param in
     * @param paopaoTunBoatInfo
     */
    public static void parsePaopaoTunInfo(ByteBuf in, PaopaoTunBoatInfo paopaoTunBoatInfo) {
//        log.info("parsePaopaoTunInfo execute.");

        byte byte1 = in.readByte();
        //读取船状态 4个bit。0：无操作，1：空闲，2：手动，3：自动，4：离线，5：维护，6-14：保留，15：无效值
        byte boatStatus = MyByteUtil.getBitValueFromByte(byte1, 4, 7);
        //读取支付状态 2个bit。
        byte payStatus = MyByteUtil.getBitValueFromByte(byte1, 2, 3);
        //读取安全须知确认状态 2个bit。
        byte safetyInstructionConfirmStatus = MyByteUtil.getBitValueFromByte(byte1, 0, 1);

        byte byte2 = in.readByte();
        //读取解锁状态 4个bit。(0：无操作，1：解锁成功，2：解锁失败，3-14：保留，15：无效值)
        byte unlockStatus = MyByteUtil.getBitValueFromByte(byte2, 4, 7);

        //读取上锁状态 4个bit。(0：无操作，1：上锁成功，2：上锁失败，3：上锁条件不满足，,4-14：保留，15：无效值)
        byte lockStatus = MyByteUtil.getBitValueFromByte(byte2, 0, 3);

        byte byte3 = in.readByte();
        //读取自动进港状态 4个bit。
        byte autoEntryPortStatus = MyByteUtil.getBitValueFromByte(byte3, 4, 7);
        //读取自动航线状态 4个bit。
        byte autoShiplineStatus = MyByteUtil.getBitValueFromByte(byte3, 0, 3);

        //保留 5个字节
        in.skipBytes(5);

        //读取任务数据更新时间。6个字节
        Date taskDataUpdateTime = readTimeFromByteBuf(in);

        //保留 2个字节
        in.skipBytes(2);

        //读取电子围栏数据更新时间。
        Date fenceUpdateTime = readTimeFromByteBuf(in);

        //保留 2个字节
        in.skipBytes(2);

        //读取单次时长。单位：秒。
        int onceDuration = in.readUnsignedShort();
        //读取累计时长。单位：秒。
        long aggregateDuration = in.readUnsignedInt();

        //保留 2个字节
        in.skipBytes(2);

        //读取单次里程。单位：100米。
        int onceDistance = in.readUnsignedShort();
        //读取累计里程。单位：100米。
        long aggregateDistance = in.readUnsignedInt();

        //保留 2个字节
        in.skipBytes(2);

        //读取单次耗电。单位：1W。
        int oncePowerConsumption = in.readUnsignedShort();
        //读取累计耗电。单位：1W。
        long aggregatePowerConsumption = in.readUnsignedInt();

        //保留 2个字节
        in.skipBytes(2);

        //读取剩余时间。单位：1秒。
        int remainTime = in.readUnsignedShort();
        //读取剩余里程。单位：100米。
        int remainDistance = in.readUnsignedShort();
        //读取返回时间。单位：1秒。
        int backTime = in.readUnsignedShort();
        //读取返回里程。单位：100米。
        int backDistance = in.readUnsignedShort();

        //读取码头配置数据更新时间。6个字节
        Date wharfConfigDataUpdateTime = readTimeFromByteBuf(in);

        byte byte4 = in.readByte();

        //读取电子围栏报警状态 2个bit。0：无报警，1：黄区报警，2：红区报警，3：无效值
        byte fenceAlarmStatus = MyByteUtil.getBitValueFromByte(byte4, 6, 7);

        //保留 1个字节
        in.skipBytes(1);

        paopaoTunBoatInfo.setBoatStatus(boatStatus);
        paopaoTunBoatInfo.setPayStatus(payStatus);
        paopaoTunBoatInfo.setSafetyInstructionConfirStatus(safetyInstructionConfirmStatus);
        paopaoTunBoatInfo.setUnlockStatus(unlockStatus);
        paopaoTunBoatInfo.setLockStatus(lockStatus);
        paopaoTunBoatInfo.setAutoEntryPortStatus(autoEntryPortStatus);
        paopaoTunBoatInfo.setAutoShiplineStatus(autoShiplineStatus);
        paopaoTunBoatInfo.setTaskDataUpdateTime(taskDataUpdateTime);
        paopaoTunBoatInfo.setFenceUpdateTime(fenceUpdateTime);
        paopaoTunBoatInfo.setOnceDuration(onceDuration);
        paopaoTunBoatInfo.setAggregateDuration(aggregateDuration);
        paopaoTunBoatInfo.setOnceDistance(onceDistance);
        paopaoTunBoatInfo.setAggregateDistance(aggregateDistance);
        paopaoTunBoatInfo.setOncePowerConsumption(oncePowerConsumption);
        paopaoTunBoatInfo.setAggregatePowerConsumption(aggregatePowerConsumption);
        paopaoTunBoatInfo.setRemainTime(remainTime);
        paopaoTunBoatInfo.setRemainDistance(remainDistance);
        paopaoTunBoatInfo.setBackTime(backTime);
        paopaoTunBoatInfo.setBackDistance(backDistance);
        paopaoTunBoatInfo.setWharfConfigDataUpdateTime(wharfConfigDataUpdateTime);
        paopaoTunBoatInfo.setFenceAlarmStatus(fenceAlarmStatus);

//        log.debug("parsePaopaoTunInfo paopaoTunBoatInfo: {}", paopaoTunBoatInfo);
    }

    /**
     * 解析惯导数据。
     *
     * @param in
     */
    public static void parseNavigationInfo(Channel channel, ByteBuf in, NavigationInfo navigationInfo) {
//        log.info("parseNavigationData execute.");

        //UTC年 1个字节 取值范围：0-255，20表示2020，默认值0
        short year = in.readUnsignedByte();
        year += 2000;
        //UTC月 1个字节 取值范围：0-12，默认值0
        short month = in.readUnsignedByte();
        //UTC日 1个字节 取值范围：0-31，默认值0
        short day = in.readUnsignedByte();
        //UTC时 1个字节 取值范围：0-23，默认值0
        short hour = in.readUnsignedByte();
        //UTC分 1个字节 取值范围：0-59，默认值0
        short minute = in.readUnsignedByte();
        //UTC秒 1个字节 取值范围：0-59，默认值0
        short second = in.readUnsignedByte();
        //UTC毫秒 1个字节 取值范围：0-999，分辨率10ms，默认值0
        short millisecond = in.readUnsignedByte();
        millisecond *= 10;
        DateTime currTime = DateUtil.parseDateTime(String.format("%d-%d-%d %d:%d:%d:%d", year, month, day, hour, minute, second, millisecond));
        //定位卫星数量 1个字节 取值范围：0-255，默认值0
        short satelliteNum = in.readUnsignedByte();
        //航向HDG,即船首向(监控系统取此值) 2个字节 取值范围：0-359.99°，分辨率0.01°，从0开始，默认值0
        int hdg = in.readUnsignedShort();
//        hdg *= 0.01;
        //航向COG,即对地航向 2个字节 取值范围：0-359.99°，分辨率0.01°，从0开始，默认值0
        //HDG与COG这两个数据虽然都是指航向，但在有风流影响时是不一样的
        int cog = in.readUnsignedShort();
        //对地航速SOG 2个字节 取值范围：0-100kn，分辨率0.01kn，从0开始，默认值0
        //kn表示节，1节=1海里/小时=1.852公里/小时=0.514444m/s
        int sog = in.readUnsignedShort();
        //对水航速SOW 2个字节 取值范围：0-100kn，分辨率0.01kn，从0开始，默认值0
        int sow = in.readUnsignedShort();
        //俯仰 2个字节 取值范围：-90 ~ 90°，分辨率0.01°，32767对应0°，默认值32767
        in.readUnsignedShort();
        //横滚 2个字节 取值范围：-90 ~ 90°，分辨率0.01°，32767对应0°，默认值32767
        in.readUnsignedShort();
        //深沉 2个字节 取值范围：-100 ~ 100m，分辨率0.01m，32767对应0m，默认值32767
        in.readUnsignedShort();
        //转向率 2个字节 取值范围：-90 ~ 90°/s，分辨率0.01°/s，32767对应0°/s，默认值32767
        in.readUnsignedShort();
        //纬度 4个字节 取值范围：-90 ~ 90°，分辨率0.0000001°，0x7fffffff对应0°，默认值0x7fffffff
        long lat = in.readUnsignedInt();
        //经度 4个字节 取值范围：-180 ~ 180°，分辨率0.0000001°，0x7fffffff对应0°，默认值0x7fffffff
        long lon = in.readUnsignedInt();


        navigationInfo.setCurrTime(currTime);
        navigationInfo.setSatelliteNum(satelliteNum);
        navigationInfo.setHdg(hdg);
        navigationInfo.setCog(cog);
//        navigationInfo.setSog(sog);
        navigationInfo.setSog(sog);
        navigationInfo.setSow(sow);
        navigationInfo.setLat(new BigDecimal((lat - BaseCoordinateValue)).multiply(new BigDecimal("0.0000001")).setScale(6, BigDecimal.ROUND_DOWN).doubleValue());
        navigationInfo.setLon(new BigDecimal((lon - BaseCoordinateValue)).multiply(new BigDecimal("0.0000001")).setScale(6, BigDecimal.ROUND_DOWN).doubleValue());

//        log.info("parseNavigationData navigationInfo: {}", navigationInfo);
    }

    /**
     * 解析电子围栏配置信息。
     *
     * @param in
     * @param systemCode
     * @param deviceCode
     */
    public static void parseFenceInfo(ByteBuf in, BoatMessage boatMessage, byte systemCode, byte deviceCode) {
        try {
            FenceInfo fenceInfo = new FenceInfo(systemCode, deviceCode);
            int toDeviceType = in.readUnsignedShort();
            byte behaviorType = in.readByte();
            //读取配置读操作 2个bit。取值范围：0-3，可能取值：0：无操作，1：配置读，3：无效值
            byte readFlag = MyByteUtil.getBitValueFromByte(in.readByte(), 6, 7);
            //读取配置报文应答 2个bit。0：无操作，1：报文应答，2：报文未应答，3：无效值
            byte responseFlag = MyByteUtil.getBitValueFromByte(in.readByte(), 6, 7);
            //保留 6个字节
            in.skipBytes(6);
            byte year = in.readByte();
            byte month = in.readByte();
            byte day = in.readByte();
            byte hour = in.readByte();
            byte minute = in.readByte();
            byte second = in.readByte();
            DateTime createOrUpdateTime = DateUtil.parseDateTime(String.format("%d-%d-%d %d:%d:%d", year + 2000, month, day, hour, minute, second));
            //保留 2个字节
            in.skipBytes(2);
            //读取电子围栏边界挂起的保护方式
            byte type = MyByteUtil.getBitValueFromByte(in.readByte(), 6, 7);
            //读取电子围栏绿区安全距离。
            int greenDistance = in.readUnsignedShort();
            //读取电子围栏黄区安全距离。
            int yellowDistance = in.readUnsignedShort();
            //读取电子围栏边界点数量(即坐标点的个数)。
            short coordinateNum = in.readUnsignedByte();
            //保留 2个字节
            in.skipBytes(2);
            List<Double> coordinates = fenceInfo.getCoordinates();
            //读取坐标点
            double item;
            for (int i = 0; i < coordinateNum * 2; i++) {
                item = new BigDecimal((in.readUnsignedInt() - BaseCoordinateValue)).multiply(new BigDecimal("0.0000001")).setScale(6, BigDecimal.ROUND_DOWN).doubleValue();
                coordinates.add(item);
            }

            fenceInfo.setToDeviceType(toDeviceType);
            fenceInfo.setBehaviorType(behaviorType);
            fenceInfo.setReadFlag(readFlag);
            fenceInfo.setResponseFlag(responseFlag);
            fenceInfo.setCreateOrUpdateTime(createOrUpdateTime);
            fenceInfo.setType(type);
            fenceInfo.setGreenDistance(greenDistance);
            fenceInfo.setYellowDistance(yellowDistance);
            fenceInfo.setCoordinateNum(coordinateNum);

            boatMessage.getDataPieceList().add(fenceInfo);
        } catch (Exception e) {
//            throw new RuntimeException(e);
            log.error("parseFenceInfo e: {}", e.getMessage());
        }
    }

    public static void encodePayStatusInfo(ByteBuf out, BaseDataPiece dataPiece) {
        PayStatusInfo payStatusInfo = (PayStatusInfo) dataPiece;
        out.writeByte(payStatusInfo.getPayStatus());
        for (int i = 0; i < 7; i++) {
            out.writeByte(0xff);
        }
    }

    public static void encodeFenceInfo(ByteBuf out, BaseDataPiece dataPiece) {
        FenceInfo fenceInfo = (FenceInfo) dataPiece;

        //写入作用的设备类别编号。2个字节
        out.writeShort(fenceInfo.getToDeviceType());
        /**
         * 写入作用的行为编号。1个字节。
         */
        out.writeByte(fenceInfo.getBehaviorType());

        //写入配置读操作 2个bit。
        out.writeByte(fenceInfo.getReadFlag());
        //写入配置报文应答
        out.writeByte(fenceInfo.getResponseFlag());
        //保留 6个字节
        for (int i = 0; i < 6; i++) {
            out.writeByte(0xff);
        }
        //写入电子围栏创建或更新的年月日时分秒 6个字节
        Date date = fenceInfo.getCreateOrUpdateTime();
        if (Objects.nonNull(date)) {
            writeTimeToByteBuf(date, out);
        } else {
            writeTimeToByteBuf(new Date(), out);
        }

        //保留 2个字节
        for (int i = 0; i < 2; i++) {
            out.writeByte(0xff);
        }
        //写入电子围栏边界挂起的保护方式，占用1个字节中的2个bit
        out.writeByte(fenceInfo.getType());
        //写入电子围栏绿区安全距离。2个字节
        out.writeShort(fenceInfo.getGreenDistance());
        //写入电子围栏黄区安全距离。2个字节
        out.writeShort(fenceInfo.getYellowDistance());
        int coordinateSize = fenceInfo.getCoordinates().size();
        //写入电子围栏边界点数量。1个字节
        out.writeByte(coordinateSize / 2);
        //保留 2个字节
        for (int i = 0; i < 2; i++) {
            out.writeByte(0xff);
        }

        Double[] coordinates = fenceInfo.getCoordinates().stream().toArray(Double[]::new);

        //电子围栏是否需要固定起止点。0：不需要，1：需要。
        int isTwoFixedPointsNeed = fenceInfo.getIsTwoFixedPointsNeed();
        int length = coordinates.length;
        //此处需判断是否需要固定起止点，如果需要则需先对coordinates中的坐标点进行重排序，不需要则不用重排序
        if (isTwoFixedPointsNeed == 1) {
            int num = coordinates.length / 2 - 1;
            for (int i = 0; i < num / 2; i++) {
                double temp1 = coordinates[2 * i + 2];
                double temp2 = coordinates[2 * i + 3];
                coordinates[2 * i + 2] = coordinates[length - 2 * i - 2];
                coordinates[2 * i + 3] = coordinates[length - 2 * i - 1];
                coordinates[length - 2 * i - 2] = temp1;
                coordinates[length - 2 * i - 1] = temp2;
            }
        }

        long item;
        for (int i = 0; i < length; i++) {
//            item = (long) (fenceInfo.getCoordinates().get(i) * 10000000 + BaseCoordinateValue);
            item = (long) (coordinates[i] * 10000000 + BaseCoordinateValue);
            out.writeBytes(ByteUtil.longToBytes(item, ByteOrder.BIG_ENDIAN), 4, 4);
        }
    }

    /**
     * 解析电池组数据。
     *
     * @param in
     * @param batteryInfo
     */
    public static void parseBatteryInfo(ByteBuf in, BatteryInfo batteryInfo) {
        //相关状态信息1 1个字节。
        byte statusInfoByte1 = in.readByte();
        //设备自检状态 4个bit。取值范围：0-15，可能取值：0：无操作，1：正常，2：异常，15：无效值
        byte selfTestStatus = MyByteUtil.getBitValueFromByte(statusInfoByte1, 4, 7);
        //保留字段 7个字节。
        in.skipBytes(7);
        //电池电压 2个字节。分辨率：0.01V，默认值：0.
        int batteryVoltage = in.readUnsignedShort();
        //电池电流 2个字节 其值可能为负数。分辨率：0.01A，默认值：0. 负数表示放电方向电流，正数表示充电方向电流，32767表示0A
        int batteryCurrentVal = in.readUnsignedShort();
        double batteryCurrent = (batteryCurrentVal - 32767 - 32767) * 0.01;
        //允许最大输出功率 2个字节。分辨率：1W
        int allowedMaximumOutputPower = in.readUnsignedShort();
        //SOC(电池电量百分比) 1个字节。分辨率：1%
        byte soc = in.readByte();
        //保留字段 1个字节。
        in.readByte();
        //电池欠压保护值 2个字节。
        in.skipBytes(2);
        //保留字段 6个字节。
        in.skipBytes(6);
        //剩余放电时间 2个字节。分辨率：1min
        int remainingUsableTime = in.readUnsignedShort();
        //最高温度，亦表示当前温度。 1个字节。分辨率：1℃，127表示0℃
        int maximumTemperature = in.readUnsignedByte() - 127;
        //最低温度 1个字节。分辨率：1℃，127表示0℃
        int minimumTemperature = in.readUnsignedByte() - 127;

        //相关状态信息2 1个字节。
        byte statusInfoByte2 = in.readByte();
        //电池告警状态 2个bit。 可能值：0：无操作，1：正常，2：异常，3：无效值
        byte alarmStatus = MyByteUtil.getBitValueFromByte(statusInfoByte2, 6, 7);
        //电池电压状态 2个bit。 可能值：0：电压正常，1：电压过低，3：电压过高，3：无效值
        byte voltageStatus = MyByteUtil.getBitValueFromByte(statusInfoByte2, 4, 5);
        //电池电流状态 2个bit。 0：无操作，1：电流正常，2：过流保护，3：无效值
        byte currentStatus = MyByteUtil.getBitValueFromByte(statusInfoByte2, 2, 3);
        //电池温度状态 2个bit。 0：温度正常，1：低温保护，2：过温保护，3：无效值
        byte temperatureStatus = MyByteUtil.getBitValueFromByte(statusInfoByte2, 0, 1);
        //相关状态信息3 1个字节。
        byte statusInfoByte3 = in.readByte();
        //电池SOC(电池电量百分比)状态  2个bit。 0：无操作，1：正常，2：异常，3：无效值
        byte socStatus = MyByteUtil.getBitValueFromByte(statusInfoByte3, 6, 7);
        //电池高温充电限流状态  2个bit。 0：无操作，1：正常，2：异常，3：无效值
        byte limitCurrentStatus = MyByteUtil.getBitValueFromByte(statusInfoByte3, 4, 5);
        //保留字段 2个字节。
        in.skipBytes(2);
        //一级故障代码 8个字节。
        in.skipBytes(8);
        //二级故障代码 8个字节。
        in.skipBytes(8);
        //三级故障代码 8个字节。
        in.skipBytes(8);

        batteryInfo.setSelfTestStatus(selfTestStatus);
        batteryInfo.setBatteryVoltage(batteryVoltage);
        batteryInfo.setBatteryCurrent(batteryCurrent);
        batteryInfo.setAllowedMaximumOutputPower(allowedMaximumOutputPower);
        batteryInfo.setSoc(soc);
        batteryInfo.setRemainingUsableTime(remainingUsableTime);
        batteryInfo.setMaximumTemperature(maximumTemperature);
        batteryInfo.setMinimumTemperature(minimumTemperature);
        batteryInfo.setAlarmStatus(alarmStatus);
        batteryInfo.setVoltageStatus(voltageStatus);
        batteryInfo.setTemperatureStatus(temperatureStatus);
        batteryInfo.setSocStatus(socStatus);
        batteryInfo.setLimitCurrentStatus(limitCurrentStatus);

//        log.info("parseBatteryData batteryInfo: {}", batteryInfo);
    }

    public static void parseBoatErrorCodeInfo(ByteBuf in, BoatErrorCodeInfo boatErrorCodeInfo) {
        List<BoatErrorCodeInfo.BoatErrorCode> boatErrorCodeList = boatErrorCodeInfo.getBoatErrorCodeList();
        for (int i = 0; i < 10; i++) {
            BoatErrorCodeInfo.BoatErrorCode boatErrorCode = new BoatErrorCodeInfo.BoatErrorCode();
//            byte[] bytes1 = new byte[2];
//            in.readBytes(bytes1, 0, 2);
            byte firthByte = in.readByte();
            byte secondByte = in.readByte();
            byte thirdByte = in.readByte();
            StringBuilder spnBuilder = new StringBuilder();
            spnBuilder.append(MyByteUtil.byteToBinStr(thirdByte).substring(0, 3));
            spnBuilder.append(MyByteUtil.byteToBinStr(secondByte));
            spnBuilder.append(MyByteUtil.byteToBinStr(firthByte));

//            log.info("builder : {}", builder);
            String spnBinStr = spnBuilder.toString();
//            log.debug("spnBinStr: {}", spnBinStr);
            int spn = Integer.parseInt(spnBinStr, 2);
            byte fmi = MyByteUtil.getBitValueFromByte(thirdByte, 3, 7);

            byte fourthByte = in.readByte();
            byte oc = MyByteUtil.getBitValueFromByte(fourthByte, 1, 7);
            byte cm = MyByteUtil.getBitValueFromByte(fourthByte, 0);

            boatErrorCode.setSpn(spn);
            boatErrorCode.setFmi(fmi);
            boatErrorCode.setOc(oc);
            boatErrorCode.setCm(cm);

            boatErrorCodeList.add(boatErrorCode);
        }

        log.debug("boatErrorCodeInfo: {}", boatErrorCodeInfo);
    }

    public static void encodeNavigationInfo(ByteBuf out, BaseDataPiece dataPiece) {
        NavigationInfo navigationInfo = (NavigationInfo) dataPiece;
        //写入电子围栏创建或更新的年月日时分秒
        Date date = navigationInfo.getCurrTime();
        out.writeByte((byte) (DateUtil.year(date) - 2000));
        out.writeByte((byte) (DateUtil.month(date) + 1));
        out.writeByte((byte) DateUtil.dayOfMonth(date));
        out.writeByte((byte) DateUtil.hour(date, true));
        out.writeByte((byte) DateUtil.minute(date));
        out.writeByte((byte) DateUtil.second(date));
        out.writeByte((byte) DateUtil.millisecond(date) / 10);

        out.writeByte(navigationInfo.getSatelliteNum());
        out.writeShort(navigationInfo.getHdg());
        out.writeShort(navigationInfo.getCog());
        out.writeShort(navigationInfo.getSog());
        out.writeShort(navigationInfo.getSow());
        for (int i = 0; i < 8; i++) {
            out.writeByte(0xff);
        }
        //写入纬度 4个字节
        out.writeBytes(ByteUtil.longToBytes((long) (navigationInfo.getLat() * 10000000 + BaseCoordinateValue), ByteOrder.BIG_ENDIAN), 4, 4);
        //写入经度 4个字节
        out.writeBytes(ByteUtil.longToBytes((long) (navigationInfo.getLon() * 10000000 + BaseCoordinateValue), ByteOrder.BIG_ENDIAN), 4, 4);
    }

    /**
     * 解析推进器数据。
     *
     * @param in
     * @param propellerInfo
     */
    public static void parsePropellerInfo(ByteBuf in, PropellerInfo propellerInfo) {
        //相关状态信息1 1个字节。
        byte statusInfoByte1 = in.readByte();
        //栅格状态 2个bit。 可能值：0：无操作，1：栅格打开，2：栅格关闭，3：无效值
        byte gridStatus = MyByteUtil.getBitValueFromByte(statusInfoByte1, 6, 7);
        //舵角 2个字节 取值范围：-90° ~ 90° 分辨率：0.01° 32767对应0°
        short rudderAngle = in.readShort();
        //翻斗/抬升百分比 1个字节 取值范围：-100%-100% 分辨率：1% 中间位置对应0%，最上位置对应100%，最下位置对应-100%，127代表0%
        byte tippingBucketPercent = in.readByte();
        //舵角百分比 1个字节 取值范围：-100%-100% 分辨率：1% -100%表示左舵最大角度，0%表示舵角0°，100%表示右舵最大角度，127代表0%
        byte rudderAnglePercent = in.readByte();
        //保留字段 3个字节
        in.skipBytes(3);

        propellerInfo.setGridStatus(gridStatus);
        propellerInfo.setRudderAngle(rudderAngle);
        propellerInfo.setTippingBucketPercent(tippingBucketPercent);
        propellerInfo.setRudderAnglePercent(rudderAnglePercent);
        log.info("parsePropellerInfo propellerInfo: {}", propellerInfo);
    }

    /**
     * 解析船首侧推数据。
     *
     * @param in
     * @param bowThrust
     */
    public static void parseBowThrust(ByteBuf in, BowThrust bowThrust) {
        //侧推百分比 1个字节 取值范围：-100%~100% 分辨率：1%  -100%：左最大，0%：停止，100%：右
        byte thrustPercent = in.readByte();
        //侧推控制频率 取值范围：0-25kHz 分辨率：0.1kHz  默认：0
        short thrustFrequency = in.readUnsignedByte();
        //侧推控制占空比 取值范围：0-100% 分辨率：1%  默认：0
        short thrustBlankPercent = in.readUnsignedByte();
        //保留字段 5个字节
        in.skipBytes(5);

        bowThrust.setThrustPercent(thrustPercent);
        bowThrust.setThrustFrequency(thrustFrequency);
        bowThrust.setThrustBlankPercent(thrustBlankPercent);
        log.info("parseBowThrust bowThrust: {}", bowThrust);
    }

    /**
     * 读取日期时间数据。6个字节
     *
     * @param in
     * @return
     */
    private static Date readTimeFromByteBuf(ByteBuf in) {
        DateTime result = DateUtil.parseDateTime("2000-01-01 08:00:00");
        byte year = in.readByte();
        byte month = in.readByte();
        byte day = in.readByte();
        byte hour = in.readByte();
        byte minute = in.readByte();
        byte second = in.readByte();
//        log.info("year: {},month: {},day: {},hour: {},minute: {},second: {}", year, month, day, hour, minute, second);
        try {
            result = DateUtil.parseDateTime(String.format("%d-%d-%d %d:%d:%d", year + 2000, month, day, hour, minute, second));
        } catch (Exception e) {
//            throw new RuntimeException(e);
            log.warn("报文时间解析出错，e: {}", e.getMessage(), e);
        }

        return result;
    }

    private static void writeTimeToByteBuf(Date date, ByteBuf out) {
        out.writeByte((byte) (DateUtil.year(date) - 2000));
        out.writeByte((byte) (DateUtil.month(date) + 1));
        out.writeByte((byte) DateUtil.dayOfMonth(date));
        out.writeByte((byte) DateUtil.hour(date, true));
        out.writeByte((byte) DateUtil.minute(date));
        out.writeByte((byte) DateUtil.second(date));
    }

    public static void encodeShiplineInfo(ByteBuf out, BaseDataPiece dataPiece) {
        ShiplineInfo shiplineInfo = (ShiplineInfo) dataPiece;
        //写入电子围栏创建或更新的年月日时分秒 6个字节
        Date date = shiplineInfo.getCreateOrUpdateTime();
        if (Objects.nonNull(date)) {
            writeTimeToByteBuf(date, out);
        } else {
            writeTimeToByteBuf(new Date(), out);
        }

        //写入任务编号 1个字节
        out.writeByte(0);
        //保留 1个字节
        out.writeByte(0xff);
        int coordinateSize = shiplineInfo.getCoordinates().size();
        long item;
        for (int i = 0; i < coordinateSize; i++) {
            item = (long) (shiplineInfo.getCoordinates().get(i) * 10000000 + BaseCoordinateValue);
            out.writeBytes(ByteUtil.longToBytes(item, ByteOrder.BIG_ENDIAN), 4, 4);
        }
    }

    public static void encodeWharfInfo(ByteBuf out, BaseDataPiece dataPiece) {
        WharfInfo wharfInfo = (WharfInfo) dataPiece;
        //写入码头创建或更新的年月日时分秒 6个字节
        Date date = wharfInfo.getCreateOrUpdateTime();
        writeTimeToByteBuf(date, out);
        //保留 2个字节
        for (int i = 0; i < 2; i++) {
            out.writeByte(0xff);
        }
        //写入码头编号 1个字节
        out.writeByte(wharfInfo.getWharfCode());
        //保留 7个字节
        for (int i = 0; i < 7; i++) {
            out.writeByte(0xff);
        }
        //写入码头坐标的纬度 4个字节
        long lat = (long) (wharfInfo.getLat() * 10000000 + BaseCoordinateValue);
        out.writeBytes(ByteUtil.longToBytes(lat, ByteOrder.BIG_ENDIAN), 4, 4);

        //写入码头坐标的经度 4个字节
        long lon = (long) (wharfInfo.getLon() * 10000000 + BaseCoordinateValue);
        out.writeBytes(ByteUtil.longToBytes(lon, ByteOrder.BIG_ENDIAN), 4, 4);
    }

    public static void parseShiplineInfo(Channel channel, ByteBuf in, BoatMessage boatMessage, short dataPieceLength, ShiplineInfo shiplineInfo) {
//        byte year = in.readByte();
//        byte month = in.readByte();
//        byte day = in.readByte();
//        byte hour = in.readByte();
//        byte minute = in.readByte();
//        byte second = in.readByte();
//        DateTime createOrUpdateTime = DateUtil.parseDateTime(String.format("%d-%d-%d %d:%d:%d", year + 2000, month, day, hour, minute, second));
        Date createOrUpdateTime = readTimeFromByteBuf(in);
        log.info("createOrUpdateTime: {}", createOrUpdateTime);

        //读取任务编号 1个字节
        in.readByte();
        //保留 1个字节
        in.readByte();
        List<Double> coordinates = shiplineInfo.getCoordinates();
        short coordinateNum = (short) ((dataPieceLength - 18) / 8);
        double item;
        for (int i = 0; i < coordinateNum * 2; i++) {
            item = new BigDecimal((in.readUnsignedInt() - BaseCoordinateValue)).multiply(new BigDecimal("0.0000001")).setScale(6, BigDecimal.ROUND_DOWN).doubleValue();
            coordinates.add(item);
        }

        shiplineInfo.setCoordinateNum(coordinateNum);
        boatMessage.getDataPieceList().add(shiplineInfo);

    }

    public static void parseBehaviorControllerInfo(Channel channel, ByteBuf in, BoatMessage boatMessage, short dataPieceLength, BehaviorControllerInfo dataPiece) {
        byte byte1 = in.readByte();
        //急停状态 2个bit。 可能值：0：无操作，1：急停，2：非急停，3：无效值
        byte currUrgencyStopStatus = MyByteUtil.getBitValueFromByte(byte1, 6, 7);

        //管理员权限 2个bit。 可能值：0：无操作，1：使能，2：禁止，3：无效值
        byte administratorPrivilege = MyByteUtil.getBitValueFromByte(byte1, 4, 5);

        byte byte2 = in.readByte();

        //读取其他剩余的7个字节
        in.skipBytes(7);

        SocketChannelManager.ChannelInfo channelInfo = SocketChannelManager.getChannelInfoBySocketChannel(channel);
        if (Objects.nonNull(channelInfo)) {
            byte previousUrgencyStopStatus = channelInfo.getUrgencyStopStatus();
            SysBoat sysBoat = SocketChannelManager.getSysBoatBySocketChannel(channel);
            if (previousUrgencyStopStatus != 1 && currUrgencyStopStatus == 1) {
                //触发急停
                log.info("触发急停");
                if (Objects.nonNull(sysBoat)) {
                    //创建BoatAlarmInfo对象
                    BoatAlarmInfo boatAlarmInfo = new BoatAlarmInfo();
                    boatAlarmInfo.setBoatIdentityId(sysBoat.getBoatIdentityId());
                    boatAlarmInfo.setBoatId(sysBoat.getBoatId());
                    boatAlarmInfo.setBoatCode(sysBoat.getBoatCode());
                    boatAlarmInfo.setScenicId(sysBoat.getScenicId());
                    boatAlarmInfo.setType(AlarmType.Urgency_Stop_Alarm); //设置报警类型。0：电子围栏，1，紧急急停，2：电机报警。
                    IUserBoatOrderInfoService userBoatOrderInfoService = SpringUtils.getBean(IUserBoatOrderInfoService.class);
                    UserBoatOrderInfo userBoatOrderInfo = userBoatOrderInfoService.getUserBoatOrderInfoOfUsingStatusByBoatIdentityId(sysBoat.getBoatIdentityId());
                    if (Objects.nonNull(userBoatOrderInfo)) {
                        boatAlarmInfo.setPhone(userBoatOrderInfo.getUserPhone());
                    }
                    boatAlarmInfo.setLevel(AlarmLevel.Urgent_Type);  //设置报警级别。0：普通，1：警告，2：紧急。
                    boatAlarmInfo.setStatus(AlarmResolveStatus.Unresolve); //设置报警的处理状态。0：未处理，1：已消除。2，已处理
                    boatAlarmInfo.setContent("紧急停船");

                    IBoatAlarmInfoService boatAlarmInfoService = SpringUtils.getBean(IBoatAlarmInfoService.class);
                    //将报警信息插入到数据库中保存
                    if (Objects.nonNull(boatAlarmInfoService)) {
                        boatAlarmInfoService.insertBoatAlarmInfo(boatAlarmInfo);
                    }

                    //给景区监控中心发送船只报警状态信息
                    WebSocketMessageHandler.sendMsgToScenicAdmin(sysBoat.getScenicId(), boatAlarmInfo);
                }
            } else if (previousUrgencyStopStatus == 1 && currUrgencyStopStatus != 1) {
                //关闭急停
                log.info("关闭急停");
                if (Objects.nonNull(sysBoat)) {
                    //创建BoatAlarmInfo对象
                    BoatAlarmInfo boatAlarmInfo = new BoatAlarmInfo();
                    boatAlarmInfo.setBoatIdentityId(sysBoat.getBoatIdentityId());
                    boatAlarmInfo.setBoatId(sysBoat.getBoatId());
                    boatAlarmInfo.setBoatCode(sysBoat.getBoatCode());
                    boatAlarmInfo.setScenicId(sysBoat.getScenicId());
                    boatAlarmInfo.setType(AlarmType.Urgency_Stop_Alarm); //设置报警类型。0：电子围栏，1，紧急急停，2：电机报警。
                    IUserBoatOrderInfoService userBoatOrderInfoService = SpringUtils.getBean(IUserBoatOrderInfoService.class);
                    UserBoatOrderInfo userBoatOrderInfo = userBoatOrderInfoService.getUserBoatOrderInfoOfUsingStatusByBoatIdentityId(sysBoat.getBoatIdentityId());
                    if (Objects.nonNull(userBoatOrderInfo)) {
                        boatAlarmInfo.setPhone(userBoatOrderInfo.getUserPhone());
                    }
                    boatAlarmInfo.setLevel(AlarmLevel.Common_Type);  //设置报警级别。0：普通，1：警告，2：紧急。
                    boatAlarmInfo.setStatus(AlarmResolveStatus.Cancelled); //设置报警的处理状态。0：未处理，1：已消除。2，已处理
                    boatAlarmInfo.setContent("紧急停船");

                    IBoatAlarmInfoService boatAlarmInfoService = SpringUtils.getBean(IBoatAlarmInfoService.class);
                    //将报警信息插入到数据库中保存
                    if (Objects.nonNull(boatAlarmInfoService)) {
                        boatAlarmInfoService.insertBoatAlarmInfo(boatAlarmInfo);
                    }

                    //给景区监控中心发送船只报警状态信息
                    WebSocketMessageHandler.sendMsgToScenicAdmin(sysBoat.getScenicId(), boatAlarmInfo);
                }
            }

            channelInfo.setUrgencyStopStatus(currUrgencyStopStatus);
        }
    }

    /**
     * 发送socket消息报文。
     *
     * @param channel
     * @param boatMessage
     */
    public static void sendSocketMsg(Channel channel, BoatMessage boatMessage) {
        log.info("sendSocketMsg 线程名：{}, time: {}", Thread.currentThread().getName(), DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        log.info("sendSocketMsg channel: {}, boatMessage: {}", channel, boatMessage);
        if (Objects.nonNull(channel) && Objects.nonNull(boatMessage)) {
            boatMessage.setMessageId((short) MessageSeq.getAndIncrement());
            channel.writeAndFlush(boatMessage);
        }
    }

    /**
     * 给指定在线船只发送码头数据。如果sysWharf为null表示未设置码头或删除了码头。
     *
     * @param boatId
     * @param sysWharf
     */
    public static void sendWharfInfoToAssignedOnlineBoat(Long boatId, SysWharf sysWharf) {
        log.info("sendWharfInfoToAssignedOnlineBoat execute. boatId: {}, sysWharf: {}", boatId, sysWharf);
        BoatMessage boatMessage = new BoatMessage(MessageType.CONFIG_MESSAGE, (byte) 1);
        WharfInfo wharfInfo = new WharfInfo();
        if (Objects.nonNull(sysWharf)) {
            Date updateTime = sysWharf.getUpdateTime();
            if (Objects.nonNull(updateTime)) {
                wharfInfo.setCreateOrUpdateTime(updateTime);
            } else {
                Date createTime = sysWharf.getCreateTime();
                if (Objects.nonNull(createTime)) {
                    wharfInfo.setCreateOrUpdateTime(createTime);
                } else {
                    wharfInfo.setCreateOrUpdateTime(new Date());
                }
            }

            wharfInfo.setLat(sysWharf.getLat().doubleValue());
            wharfInfo.setLon(sysWharf.getLon().doubleValue());
            wharfInfo.setWharfCode(sysWharf.getWharfCode().charAt(0));
        } else {
            wharfInfo.setCreateOrUpdateTime(new Date());
            wharfInfo.setLat((double) 0);
            wharfInfo.setLon((double) 0);
            wharfInfo.setWharfCode('A');
        }

        List<BaseDataPiece> dataPieceList = boatMessage.getDataPieceList();
        dataPieceList.add(wharfInfo);

        Channel channel = SocketChannelManager.getSocketChannelByBoatId(boatId);
        if (Objects.nonNull(channel)) {
            SocketMessageHandler.sendSocketMsg(channel, boatMessage);
        }
    }

    /**
     * 向指定景区当前所有在线船只发送新增或更新后的码头信息。
     *
     * @param sysWharf
     */
    public static void sendWharfInfoToAllOnlineBoatOfAssignedScenic(SysWharf sysWharf) {
        log.info("sendWharfInfoToAllOnlineBoat execute. sysWharf: {}", sysWharf);
        BoatMessage boatMessage = new BoatMessage(MessageType.CONFIG_MESSAGE, (byte) 1);
        WharfInfo wharfInfo = new WharfInfo();
        Date updateTime = sysWharf.getUpdateTime();
        if (Objects.nonNull(updateTime)) {
            wharfInfo.setCreateOrUpdateTime(updateTime);
        } else {
            Date createTime = sysWharf.getCreateTime();
            if (Objects.nonNull(createTime)) {
                wharfInfo.setCreateOrUpdateTime(createTime);
            } else {
                wharfInfo.setCreateOrUpdateTime(new Date());
            }
        }

        wharfInfo.setLat(sysWharf.getLat().doubleValue());
        wharfInfo.setLon(sysWharf.getLon().doubleValue());
        wharfInfo.setWharfCode(sysWharf.getWharfCode().charAt(0));

        List<BaseDataPiece> dataPieceList = boatMessage.getDataPieceList();
        dataPieceList.add(wharfInfo);

        SocketChannelManager.sendMsgToAllOnlineBoatOfAssignedScenic(sysWharf.getScenicId(), boatMessage);

        Long scenicIdOfWharf = sysWharf.getScenicId();
        Long wharfId = sysWharf.getWharfId();

        Map<Channel, SysBoat> socketChannelAndSysBoatMap = SocketChannelManager.getSocketChannelAndSysBoatMap();
        for (Channel channel : socketChannelAndSysBoatMap.keySet()) {
            SysBoat sysBoat = socketChannelAndSysBoatMap.get(channel);
            //需确保船只所属的景区id与当前码头所属的景区id一致
            boolean isScenicAccord = Objects.equals(sysBoat.getScenicId(), scenicIdOfWharf);
            if (isScenicAccord) {
                //再需确保船只分配的码头id与当前码头id一致
                Long wharfIdOfBoat = sysBoat.getWharfId();
                if (Objects.nonNull(wharfIdOfBoat) && Objects.equals(wharfId, wharfIdOfBoat)) {
                    SocketMessageHandler.sendSocketMsg(channel, boatMessage);
                }
            }
        }
    }

    /**
     * 向指定在线船只发送电子围栏数据。
     *
     * @param boatId
     * @param sysFence
     */
    public static void sendFenceInfoToAssignedOnlineBoat(Long boatId, SysFence sysFence) {
        log.info("sendFenceInfoToAssignedOnlineBoat execute,boatId: {}, sysFence: {}", boatId, sysFence);

        FenceInfo fenceInfo = new FenceInfo();

        if (Objects.nonNull(sysFence)) {
            Long wharfId = sysFence.getWharfId();
            ISysWharfService sysWharfService = SpringUtils.getBean(ISysWharfService.class);
            SysWharf sysWharf = sysWharfService.selectWharfByWharfId(wharfId);
            if (Objects.nonNull(sysWharf)) {
                //电子围栏是否需要固定起止点。0：不需要，1：需要。
                int isTwoFixedPointsNeed = sysWharf.getIsTwoFixedPointsNeed();
                fenceInfo.setIsTwoFixedPointsNeed(isTwoFixedPointsNeed);
            }

            Date updateTime = sysFence.getUpdateTime();
            if (Objects.nonNull(updateTime)) {
                fenceInfo.setCreateOrUpdateTime(updateTime);
            } else {
                Date createTime = sysFence.getCreateTime();
                if (Objects.nonNull(createTime)) {
                    fenceInfo.setCreateOrUpdateTime(createTime);
                } else {
                    fenceInfo.setCreateOrUpdateTime(new Date());
                }
            }

            fenceInfo.setGreenDistance(sysFence.getGreenDistance());
            fenceInfo.setYellowDistance(sysFence.getYellowDistance());
            fenceInfo.setCoordinateNum(sysFence.getCoordinateNum());

            String[] coordinateArr = sysFence.getCoordinates().split(",");
            List<Double> coordinates = fenceInfo.getCoordinates();
            for (int i = 0; i < coordinateArr.length; i++) {
                coordinates.add(Double.valueOf(coordinateArr[i]));
            }
        }

        BoatMessage boatMessage = new BoatMessage(MessageType.CONFIG_MESSAGE, (byte) 1);
        List<BaseDataPiece> dataPieceList = boatMessage.getDataPieceList();
        dataPieceList.add(fenceInfo);

        Channel channel = SocketChannelManager.getSocketChannelByBoatId(boatId);
        if (Objects.nonNull(channel)) {
            sendSocketMsg(channel, boatMessage);
        }
    }

    /**
     * 向指定景区当前所有在线船只发送更新后(不包含新增的)的电子围栏信息。
     *
     * @param sysFence
     */
//    @Async("threadPoolTaskExecutor")
    public static void sendFenceInfoToAllOnlineBoatOfAssignedScenic(SysFence sysFence) {
        log.info("向所有在线船只发送电子围栏数据, sysFence: {}", sysFence);

        BoatMessage boatMessage = new BoatMessage(MessageType.CONFIG_MESSAGE, (byte) 1);
        FenceInfo fenceInfo = new FenceInfo();

        Long wharfId = sysFence.getWharfId();
        ISysWharfService sysWharfService = SpringUtils.getBean(ISysWharfService.class);
        SysWharf sysWharf = sysWharfService.selectWharfByWharfId(wharfId);
        if (Objects.nonNull(sysWharf)) {
            //电子围栏是否需要固定起止点。0：不需要，1：需要。
            int isTwoFixedPointsNeed = sysWharf.getIsTwoFixedPointsNeed();
            fenceInfo.setIsTwoFixedPointsNeed(isTwoFixedPointsNeed);
        }

        Date updateTime = sysFence.getUpdateTime();
        if (Objects.nonNull(updateTime)) {
            fenceInfo.setCreateOrUpdateTime(updateTime);
        } else {
            Date createTime = sysFence.getCreateTime();
            if (Objects.nonNull(createTime)) {
                fenceInfo.setCreateOrUpdateTime(createTime);
            } else {
                fenceInfo.setCreateOrUpdateTime(new Date());
            }
        }

        fenceInfo.setGreenDistance(sysFence.getGreenDistance());
        fenceInfo.setYellowDistance(sysFence.getYellowDistance());
        fenceInfo.setCoordinateNum(sysFence.getCoordinateNum());

        String[] coordinateArr = sysFence.getCoordinates().split(",");
        List<Double> coordinates = fenceInfo.getCoordinates();
        for (int i = 0; i < coordinateArr.length; i++) {
            coordinates.add(Double.valueOf(coordinateArr[i]));
        }

        List<BaseDataPiece> dataPieceList = boatMessage.getDataPieceList();
        dataPieceList.add(fenceInfo);

        log.info("sendFenceInfoToAllOnlineBoat boatMessage= {}", boatMessage);
        log.info("sendFenceInfoToAllOnlineBoat 线程名：{}, time: {}", Thread.currentThread().getName(),
                DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));

        SocketChannelManager.sendMsgToAllOnlineBoatOfAssignedScenic(sysFence.getScenicId(), boatMessage);

        Long scenicId = sysFence.getScenicId();
        Long fenceId = sysFence.getFenceId();

        Map<Channel, SysBoat> socketChannelAndSysBoatMap = SocketChannelManager.getSocketChannelAndSysBoatMap();
        for (Channel channel : socketChannelAndSysBoatMap.keySet()) {
            SysBoat sysBoat = socketChannelAndSysBoatMap.get(channel);
            //需确保船只所属的景区id与当前航线所属的景区id一致
            boolean isScenicAccord = Objects.equals(sysBoat.getScenicId(), scenicId);
            if (isScenicAccord) {
                //再需确保船只分配的电子围栏id与当前电子围栏id一致
                Long fenceIdOfBoat = sysBoat.getFenceId();
                if (Objects.nonNull(fenceIdOfBoat) && Objects.equals(fenceId, fenceIdOfBoat)) {
                    SocketMessageHandler.sendSocketMsg(channel, boatMessage);
                }
            }
        }
    }

    /**
     * 给指定的在线船只发送自动航线数据。
     *
     * @param boatId
     * @param sysShipline 自动航线对象，sysShipline可以为null，如果sysShipline为null表示当前船只没有设置自动航线
     */
    public static void sendShiplineInfoToAssignedOnlineBoat(Long boatId, @Nullable SysShipline sysShipline) {
        log.info("sendShiplineInfoToAssignedOnlineBoat execute, boatId: {}, sysShipline: {}", boatId, sysShipline);
        ShiplineInfo shiplineInfo = new ShiplineInfo();
        //判断sysShipline是否为null，如果sysShipline为null表示当前船只没有设置自动航线
        if (Objects.nonNull(sysShipline)) {
            Date updateTime = sysShipline.getUpdateTime();
            if (Objects.nonNull(updateTime)) {
                shiplineInfo.setCreateOrUpdateTime(updateTime);
            } else {
                Date createTime = sysShipline.getCreateTime();
                if (Objects.nonNull(createTime)) {
                    shiplineInfo.setCreateOrUpdateTime(createTime);
                } else {
                    shiplineInfo.setCreateOrUpdateTime(new Date());
                }
            }
            shiplineInfo.setCoordinateNum(sysShipline.getCoordinateNum());

            String[] coordinateArr = sysShipline.getCoordinates().split(",");
            List<Double> coordinates = shiplineInfo.getCoordinates();
            for (int i = 0; i < coordinateArr.length; i++) {
                coordinates.add(Double.valueOf(coordinateArr[i]));
            }
        }

        BoatMessage boatMessage = new BoatMessage(MessageType.CONFIG_MESSAGE, (byte) 1);
        List<BaseDataPiece> dataPieceList = boatMessage.getDataPieceList();
        dataPieceList.add(shiplineInfo);

        Channel channel = SocketChannelManager.getSocketChannelByBoatId(boatId);
        if (Objects.nonNull(channel)) {
            SocketMessageHandler.sendSocketMsg(channel, boatMessage);
        }
    }

    public static void sendShiplineInfoToAllOnlineBoat(SysShipline sysShipline) {
        log.info("sendShiplineInfoToAllOnlineBoat execute.");
        BoatMessage boatMessage = new BoatMessage(MessageType.CONFIG_MESSAGE, (byte) 1);
        ShiplineInfo shiplineInfo = new ShiplineInfo();
        Date updateTime = sysShipline.getUpdateTime();
        if (Objects.nonNull(updateTime)) {
            shiplineInfo.setCreateOrUpdateTime(updateTime);
        } else {
            Date createTime = sysShipline.getCreateTime();
            if (Objects.nonNull(createTime)) {
                shiplineInfo.setCreateOrUpdateTime(createTime);
            } else {
                shiplineInfo.setCreateOrUpdateTime(new Date());
            }
        }
        shiplineInfo.setCoordinateNum(sysShipline.getCoordinateNum());

        String[] coordinateArr = sysShipline.getCoordinates().split(",");
        List<Double> coordinates = shiplineInfo.getCoordinates();
        for (int i = 0; i < coordinateArr.length; i++) {
            coordinates.add(Double.valueOf(coordinateArr[i]));
        }

        List<BaseDataPiece> dataPieceList = boatMessage.getDataPieceList();
        dataPieceList.add(shiplineInfo);

        Long scenicId = sysShipline.getScenicId();
        Long shiplineId = sysShipline.getShiplineId();

        Map<Channel, SysBoat> socketChannelAndSysBoatMap = SocketChannelManager.getSocketChannelAndSysBoatMap();
        for (Channel channel : socketChannelAndSysBoatMap.keySet()) {
            SysBoat sysBoat = socketChannelAndSysBoatMap.get(channel);
            //需确保船只所属的景区id与当前航线所属的景区id一致
            boolean isScenicAccord = Objects.equals(sysBoat.getScenicId(), scenicId);
            if (isScenicAccord) {
                Long[] shiplineIds = sysBoat.getShiplineIds();
                if (Objects.nonNull(shiplineIds) && shiplineIds.length > 0) {
                    boolean isShiplineIdAccord = Arrays.asList(shiplineIds).contains(shiplineId);
                    if (isShiplineIdAccord) {
                        SocketMessageHandler.sendSocketMsg(channel, boatMessage);
                    }
                }
            }
        }
    }


}
